package gsw.tool.arithmetic.encode;

import java.security.Key;
import java.security.KeyPair;
import java.security.KeyPairGenerator;
import java.security.SecureRandom;

import android.os.Environment;
import android.util.Base64;

import javax.crypto.Cipher;

import gsw.tool.common.ToolFile;
import gsw.tool.common.Tool;
import gsw.tool.common.ToolShow;

/**
 * 非对称加密算法
 * 非对称加密算法是一种密钥的保密方法。
 * 非对称加密算法需要两个密钥：公开密钥（publickey）和私有密钥（privatekey）。公开密钥与私有密钥是一对，如果用公开密钥对数据进行加密，只有用对应的私有密钥才能解密；如果用私有密钥对数据进行加密，那么只有用对应的公开密钥才能解密。因为加密和解密使用的是两个不同的密钥，所以这种算法叫作非对称加密算法。 非对称加密算法实现机密信息交换的基本过程是：甲方生成一对密钥并将其中的一把作为公用密钥向其它方公开；得到该公用密钥的乙方使用该密钥对机密信息进行加密后再发送给甲方；甲方再用自己保存的另一把专用密钥对加密后的信息进行解密。
 * <p>
 * RSA加密算法
 * RSA加密算法是一种非对称加密算法,在公开密钥加密和电子商业中RSA被广泛使用。RSA算法是第一个能同时用于加密和数字签名的算法，也易于理解和操作。RSA是被研究得最广泛的公钥算法，从提出到现今的三十多年里，经历了各种攻击的考验，逐渐为人们接受，截止2017年被普遍认为是最优秀的公钥方案之一。
 * <p>
 * RSA的原理：
 * 1.公钥生成:公钥由n和e组成。n=P*Q,P和Q都是素数，例如：n=53*59=3127。e是一个不能整除n且介于1与PHI(n)=(P-1)*(Q-1)之间的整数，例如:e=3。
 * 2.私钥生成：私钥d=(2*PHI(n)+1)/e，例如：d=(2*(53-1)*(59-1)+1)/3=2011。
 * 3.使用公钥加密数据：把明文的每一位转换成数字 如HI=89，密文c=明文^e%n， 例如:c=89^3%3127=1394。
 * 4.使用私钥解密数据：明文=c^d%n， 例如:明文=1394^2011% 3127=89。
 *
 * @author GSW
 */
public class RSA {
    public static final String ALGORITHM = "RSA"; // 指定加密算法的名字
    public static final int KEYSIZE = 512; // 指定key的位数,必须是64的倍数且大于等于512

    public static void test() throws Exception {
        // 指定公钥存放的文件
        String PUBLC_KEY_FILE = Environment.getExternalStorageDirectory().getAbsolutePath() + "/test/public_key.dat";
        // 指定私钥存放的文件
        String PRIVATE_KEY_FILE = Environment.getExternalStorageDirectory().getAbsolutePath() + "/test/private_key.dat";
        createKey(PUBLC_KEY_FILE, PRIVATE_KEY_FILE);
        String s = "jett";
        String privateMsg = encode(s, PUBLC_KEY_FILE);
        ToolShow.log(privateMsg);
        String publicMsg = decode(privateMsg, PRIVATE_KEY_FILE);
        ToolShow.log(publicMsg);
    }

    /**
     * 生成密钥对
     * 这里使用KeyPairGenerator来生产密钥对，KeyPairGenerator初始化需要传入密钥位数和一个安全的随机数
     *
     * @param publicKeyFile  公钥存放的文件
     * @param privateKeyFile 私钥存放的文件
     * @throws Exception
     */
    public static void createKey(String publicKeyFile, String privateKeyFile) throws Exception {
        // 安全的随机数
        SecureRandom sr = new SecureRandom();
        // 生成密钥工具类
        KeyPairGenerator kpg = KeyPairGenerator.getInstance(ALGORITHM);
        // KEYSIZE必须是64的倍数，在512--65536之间 默认1024
        kpg.initialize(KEYSIZE, sr);
        // 生成密钥对
        KeyPair kp = kpg.generateKeyPair();
        // 获取公钥
        Key publicKey = kp.getPublic();
        // 获取私钥
        Key privateKey = kp.getPrivate();
        // 保存公钥
        ToolFile.writeObject(publicKeyFile, publicKey);
        // 保存私钥
        ToolFile.writeObject(privateKeyFile, privateKey);
    }

    /**
     * 加密
     * 使用Cipher来加密，先声明Cliper的类型为RSA加密，然后Cipher初始化需要传入模式为加密模式和之前生成的公钥
     *
     * @param msg           明文
     * @param publicKeyFile 公钥存放的文件
     * @return
     * @throws Exception
     */
    public static String encode(String msg, String publicKeyFile) throws Exception {
        if (Tool.isEmpty(msg) || Tool.isEmpty(publicKeyFile)) {
            return null;
        }
        // 获取公钥
        Key key = (Key) ToolFile.readObject(publicKeyFile);
        //设置为RSA加密类型
        Cipher cip = Cipher.getInstance(ALGORITHM);
        // 设置为加密模式，并传入公钥
        cip.init(Cipher.ENCRYPT_MODE, key);
        //加密结果
        byte[] result = cip.doFinal(msg.getBytes());
        //再使用Base64加密一次
        String s = Base64.encodeToString(result, Base64.DEFAULT);
        return s;
    }

    /**
     * 解密
     * 使用Cipher来解密，传入模式为解密模式和之前生成的私钥
     *
     * @param msg            密文
     * @param privateKeyFile 私钥存放的文件
     * @return
     * @throws Exception
     */
    public static String decode(String msg, String privateKeyFile) throws Exception {
        if (Tool.isEmpty(msg) || Tool.isEmpty(privateKeyFile)) {
            return null;
        }
        // 获取私钥
        Key key = (Key) ToolFile.readObject(privateKeyFile);
        //设置为RSA加密类型
        Cipher cip = Cipher.getInstance(ALGORITHM);
        //设置为解密模式，并传入私钥
        cip.init(Cipher.DECRYPT_MODE, key);
        //先得到Base64的解密结果
        byte[] result = Base64.decode(msg.getBytes(), Base64.DEFAULT);
        //再得到RSA的解密结果
        result = cip.doFinal(result);
        String s = new String(result);
        return s;
    }
}
